#include "warray/warray_power.h"
#include "test.h"

#include <iostream>

#include "custom_type.h"
#include "helper.h"

typedef boost::mpl::list< float,double,custom_type,std::complex<float>,std::complex<double> > test_types;
typedef boost::mpl::list< float,double > test_scalar_types;
typedef boost::mpl::list< float,double,std::complex<float>,std::complex<double> > test_complex_types;

BOOST_AUTO_TEST_CASE_TEMPLATE( array_square, T, test_types )
{
	warp::arraynd<T,1> x(warp::extents[16]);
	fill_nonzero(x);
	
	warp::arraynd<T,1> z(x.dims()); 
	warp::square(x,z);
	BOOST_CHECK_EQUAL(x.dims(),z.dims());
	BOOST_CHECK( (array_equal(x,x,z,std::multiplies<T>())) );
	
	z = warp::square(x);
	BOOST_CHECK_EQUAL(x.dims(),z.dims());
	BOOST_CHECK( (array_equal(x,x,z,std::multiplies<T>())) );
	
	z = x.copy();
	warp::square(z,z);
	BOOST_CHECK_EQUAL(x.dims(),z.dims());
	BOOST_CHECK( (array_equal(x,x,z,std::multiplies<T>())) );
}

BOOST_AUTO_TEST_CASE_TEMPLATE( array_inv, T, test_types )
{
	warp::arraynd<T,1> x(warp::extents[16]);
	fill_nonzero(x);
	
	warp::arraynd<T,1> z(x.dims()); 
	warp::inv(x,z);
	BOOST_CHECK_EQUAL(x.dims(),z.dims());
	BOOST_CHECK( (array_equal(x,z,warp::detail::inv<T>())) );
	
	z = warp::inv(x);
	BOOST_CHECK_EQUAL(x.dims(),z.dims());
	BOOST_CHECK( (array_equal(x,z,warp::detail::inv<T>())) );
	
	z = x.copy();
	warp::inv(z,z);
	BOOST_CHECK_EQUAL(x.dims(),z.dims());
	BOOST_CHECK( (array_equal(x,z,warp::detail::inv<T>())) );
}

BOOST_AUTO_TEST_CASE_TEMPLATE( array_sqrt, T, test_complex_types )
{
	warp::arraynd<T,1> x(warp::extents[16]);
	fill_nonzero(x);
	
	warp::arraynd<T,1> z(x.dims()); 
	warp::sqrt(x,z);
	BOOST_CHECK_EQUAL(x.dims(),z.dims());
	BOOST_CHECK( (array_equal(x,z,warp::detail::choose_sqrt<T>())) );
	
	z = warp::sqrt(x);
	BOOST_CHECK_EQUAL(x.dims(),z.dims());
	BOOST_CHECK( (array_equal(x,z,warp::detail::choose_sqrt<T>())) );
	
	z = x.copy();
	warp::sqrt(z,z);
	BOOST_CHECK_EQUAL(x.dims(),z.dims());
	BOOST_CHECK( (array_equal(x,z,warp::detail::choose_sqrt<T>())) );
}

BOOST_AUTO_TEST_CASE_TEMPLATE( array_invsqrt, T, test_scalar_types )
{
	warp::arraynd<T,1> x(warp::extents[16]);
	fill_nonzero(x);
	
	warp::arraynd<T,1> z(x.dims()); 
	warp::invsqrt(x,z);
	BOOST_CHECK_EQUAL(x.dims(),z.dims());
	BOOST_CHECK( (array_equal(x,z,warp::detail::invsqrt<T>())) );
	
	z = warp::invsqrt(x);
	BOOST_CHECK_EQUAL(x.dims(),z.dims());
	BOOST_CHECK( (array_equal(x,z,warp::detail::invsqrt<T>())) );
	
	z = x.copy();
	warp::invsqrt(z,z);
	BOOST_CHECK_EQUAL(x.dims(),z.dims());
	BOOST_CHECK( (array_equal(x,z,warp::detail::invsqrt<T>())) );
}

BOOST_AUTO_TEST_CASE_TEMPLATE( array_pow, T, test_complex_types )
{
	warp::arraynd<T,1> x(warp::extents[16]);
	warp::arraynd<T,1> y(x.dims());
	fill_nonzero(x);
	fill_nonzero(y);
	warp::sqrt(y,y);
	
	warp::arraynd<T,1> z(x.dims()); 
	warp::pow(x,y,z);
	BOOST_CHECK_EQUAL(x.dims(),z.dims());
	BOOST_CHECK( (array_equal(x,y,z,warp::detail::choose_pow<T>())) );
	
	z = warp::pow(x,y);
	BOOST_CHECK_EQUAL(x.dims(),z.dims());
	BOOST_CHECK( (array_equal(x,y,z,warp::detail::choose_pow<T>())) );
	
	z = x.copy();
	warp::pow(z,y,z);
	BOOST_CHECK_EQUAL(x.dims(),z.dims());
	BOOST_CHECK( (array_equal(x,y,z,warp::detail::choose_pow<T>())) );
	
	z = y.copy();
	warp::pow(x,z,z);
	BOOST_CHECK_EQUAL(x.dims(),z.dims());
	BOOST_CHECK( (array_equal(x,y,z,warp::detail::choose_pow<T>())) );
	
	T power = T(2);
	
	warp::pow(x,power,z);
	BOOST_CHECK_EQUAL(x.dims(),z.dims());
	BOOST_CHECK( (array_equal(x,z,std::bind2nd(warp::detail::choose_pow<T>(),power))) );
	
	z = warp::pow(x,power);
	BOOST_CHECK_EQUAL(x.dims(),z.dims());
	BOOST_CHECK( (array_equal(x,z,std::bind2nd(warp::detail::choose_pow<T>(),power))) );
	
	z = x.copy();
	warp::pow(z,power,z);
	BOOST_CHECK_EQUAL(x.dims(),z.dims());
	BOOST_CHECK( (array_equal(x,z,std::bind2nd(warp::detail::choose_pow<T>(),power))) );
}

BOOST_AUTO_TEST_CASE_TEMPLATE( array_hypot, T, test_scalar_types )
{
	warp::arraynd<T,1> x(warp::extents[16]);
	warp::arraynd<T,1> y(x.dims());
	fill_nonzero(x);
	fill_nonzero(y);
	
	warp::arraynd<T,1> z(x.dims()); 
	warp::hypot(x,y,z);
	BOOST_CHECK_EQUAL(x.dims(),z.dims());
	BOOST_CHECK( (array_equal(x,y,z,warp::detail::hypot<T>())) );
	
	z = warp::hypot(x,y);
	BOOST_CHECK_EQUAL(x.dims(),z.dims());
	BOOST_CHECK( (array_equal(x,y,z,warp::detail::hypot<T>())) );
	
	z = x.copy();
	warp::hypot(z,y,z);
	BOOST_CHECK_EQUAL(x.dims(),z.dims());
	BOOST_CHECK( (array_equal(x,y,z,warp::detail::hypot<T>())) );
	
	z = y.copy();
	warp::hypot(x,z,z);
	BOOST_CHECK_EQUAL(x.dims(),z.dims());
	BOOST_CHECK( (array_equal(x,y,z,warp::detail::hypot<T>())) );
}
